﻿//License:  New BSD License (BSD)
//Copyright (c) 2010, Jesper Lund Stocholm
//All rights reserved.

//Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

//* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

//* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

//* Neither the name of Jesper Lund Stocholm nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


using System;
using System.Collections.Generic;
using System.Data;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using IS29500Validator.Interfaces;
using IS29500Validator.Logging;

namespace IS29500Validator.Models
{
    public partial class OOXMLDocument : IDocument
    {
        /// <summary>
        /// Specific type of this OOXML document
        /// </summary>
        public Enums.DocumentType Type { get; set; }

        /// <summary>
        /// Status for this document validation
        /// </summary>
        public List<StatusLine> StatusLines { get; private set; }

        /// <summary>
        /// Log messages for this document validation
        /// </summary>
        public List<LogMessage> LogMessages { get; private set; }

        /// <summary>
        /// Base stream of current document
        /// </summary>
        public Stream Stream { get; set; }

        /// <summary>
        /// File information
        /// </summary>
        public FileInfo FileInfo { get; set; }

        /// <summary>
        /// Generator of this document
        /// </summary>
        public string DocumentGenerator { get; private set; }

        /// <summary>
        /// File extension of this document file
        /// </summary>
        public string Extension { get; set;}

        /// <summary>
        /// Mapping between MIME-types (content types) and the correspondiong properties of parts
        /// </summary>
        private IDictionary<string, ContentTypeRelationshipMap> RelationshipMapping { get; set; }

        private XmlSchemaSet m_xmlSchemaSet;

        /// <summary>
        /// Instantiates this document by stream
        /// </summary>
        /// <param name="documentStream_">Stream containing document</param>
        /// <param name="filedata_">Information about the file</param>
        /// <param name="relationshipMapping_">Mapping between MIME-types (content types) and the correspondiong properties of parts</param>
        /// <param name="xmlSchemaSet_">Set of schemas to validate part content against</param>
        public OOXMLDocument(
            Stream documentStream_,
            FileInfo filedata_, 
            IDictionary<string, 
            ContentTypeRelationshipMap> relationshipMapping_,
            XmlSchemaSet xmlSchemaSet_)
        {
            DocumentGenerator = "";
            this.Stream = documentStream_;
            this.RelationshipMapping = relationshipMapping_;
            m_xmlSchemaSet = xmlSchemaSet_;
            StatusLines = new List<StatusLine>();
            LogMessages = new List<LogMessage>();


            FileInfo = filedata_;

            Type = GetDocumentType();
        }

        /// <summary>
        /// Gets relationships for this package
        /// </summary>
        /// <param name="package_">Specific OPC-Package</param>
        /// <returns>A list of all relationships in this package</returns>
        List<PackageRelationship> GetRelationShips(Package package_)
        {
            var packageRelationshipsTmp = new List<PackageRelationship>();

            var packageRelationShips = package_.GetRelationships();
            packageRelationshipsTmp.AddRange(packageRelationShips);

            foreach (var rel in packageRelationShips)
            {
                if (rel.TargetMode == TargetMode.External)
                    continue;

                var uri = PackUriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri);

                var part = package_.GetPart(uri);

                packageRelationshipsTmp.AddRange(GetRelationships(part));
            }

            return packageRelationshipsTmp;
        }

        /// <summary>
        /// Get relationships for this part
        /// </summary>
        /// <param name="part_">Specific OPC-Part</param>
        /// <returns>A list of all relationships defined by this OPC-Part</returns>
        List<PackageRelationship> GetRelationships(PackagePart part_)
        {
            var partRelationshipsTmp = new List<PackageRelationship>();

            var partRelationShips = part_.GetRelationships();
            partRelationshipsTmp.AddRange(partRelationShips);

            foreach (var rel in partRelationShips)
            {
                // Only verify internal relationships
                if (rel.TargetMode == TargetMode.External)
                    continue;

                var uri = PackUriHelper.ResolvePartUri(rel.SourceUri, rel.TargetUri);
                Debug.WriteLine(uri);

                // if current relationship has alreay been dealt with
                if (!partRelationshipsTmp.Contains(rel))
                {
                    var part = part_.Package.GetPart(uri);
                    partRelationshipsTmp.AddRange(GetRelationships(part));
                }
                    

                
                
            }

            return partRelationshipsTmp;
        }
        
        /// <summary>
        /// Validates content and structure of this document
        /// </summary>
        public void Validate()
        {
            VerifyContentTypes(StatusLines);
            VerifyRelationshipTypes(StatusLines);
            VerifyPartExistance(StatusLines);
            VerifyPartContent(StatusLines);
        }

        /// <summary>
        /// Gets OOXML document type for this document
        /// </summary>
        /// <returns></returns>
        private Enums.DocumentType GetDocumentType()
        {
            switch (FileInfo.Extension.ToLower())
            {
                case ".docx":
                    return Enums.DocumentType.WordProcessingML;
                    break;
                case ".xlsx":
                    return Enums.DocumentType.SpreadsheetML;
                    break;
                case ".pptx":
                    return Enums.DocumentType.PresentationML;
                    break;
                default:
                    throw new NotSupportedException("This file extension is not supported");
            }
        }

        /// <summary>
        /// Gets the stream of this package
        /// </summary>
        /// <returns></returns>
        public Package GetIOPackage()
        {
            return Package.Open(this.Stream);
        }

        string GetGeneratingApplication(PackagePart extendedPropsPart_)
        {
            var reader = XmlReader.Create(extendedPropsPart_.GetStream());

            XNamespace ns = "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties";
            XElement xDoc = XElement.Load(XmlReader.Create(extendedPropsPart_.GetStream()));

            var application = xDoc.Element(ns + "Application");
            var appVersion = xDoc.Element(ns + "AppVersion");

            return string.Format("{0} {1}", application == null ? "" : application.Value, appVersion == null ? "" : appVersion.Value);
        }
    }
}
